# -*- coding: utf-8 -*-
# Gestión y diseño gráfico de funciones de módulo.

## vipera, un diseñador de aplicaciones para Python
##     Copyright (C) 2011 Ángel Luis García García
##
##    This program is free software: you can redistribute it and/or modify
##    it under the terms of the GNU General Public License as published by
##    the Free Software Foundation, either version 3 of the License, or
##    (at your option) any later version.
##
##    This program is distributed in the hope that it will be useful,
##    but WITHOUT ANY WARRANTY; without even the implied warranty of
##    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
##    GNU General Public License for more details.
##
##    You should have received a copy of the GNU General Public License
##    along with this program.  If not, see <http://www.gnu.org/licenses/>.

import wx
import wx.lib.scrolledpanel as scrolled
import wx.lib.agw.pybusyinfo as PBI
import wx.aui

# from editor2_vipera import panel_codigo_ed
from configuracion_vipera import proy_por_defecto
from i18n_vipera import t

class panel_funciones(scrolled.ScrolledPanel):
    '''Panel de funciones de módulo'''
    def __init__(self, parent, panel_principal, nfilas = 1, idioma = None):
        '''Constructor de clase'''
        scrolled.ScrolledPanel.__init__(self, parent, style = wx.TAB_TRAVERSAL|wx.SUNKEN_BORDER)
        # Idioma.
        self.l = idioma
        # Referencia al panel principal.
        self.panel_principal = panel_principal
        # ###############################
        # Modelo de gestión de funciones.
        # modelo = [[funcion, comentario, 
        #            pre, post, codigo, 
        #            [(param, por_defecto, tipo_dato)...]]
        #          ...]
        # Creamos modelo.
        self.modelo = list()
        # ###############################        
        # Lista de TextCtrl.
        self.control_texto = []
        # Definimos sizer principal.
        sizer_p = wx.BoxSizer(wx.VERTICAL)
        # [19-05-2011] FlexGridSizer de comentarios/precondición/postcondición.
        fgs_p = wx.FlexGridSizer(6,1,5,5)
        fgs_p.AddGrowableCol(0)
        fgs_p.AddGrowableRow(0)
        fgs_p.AddGrowableRow(1)
        fgs_p.AddGrowableRow(2)
        fgs_p.AddGrowableRow(3)
        fgs_p.AddGrowableRow(4)
        fgs_p.AddGrowableRow(5)
        fgs_p.SetFlexibleDirection(wx.BOTH)
        fgs_p.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED)
        # Cajas de texto...
        self.m_staticText4 = wx.StaticText( self, wx.ID_ANY, t(u"Comentario", self.l), wx.DefaultPosition, wx.DefaultSize, 0 )
        self.m_staticText4.Wrap( -1 )
        fgs_p.Add( self.m_staticText4, 0, wx.ALL|wx.EXPAND, 5 )
        self.m_textCtrl_coment = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_MULTILINE )
        fgs_p.Add( self.m_textCtrl_coment, 0, wx.ALL|wx.EXPAND, 5 )
        self.m_staticText5 = wx.StaticText( self, wx.ID_ANY, t(u"Precondición", self.l), wx.DefaultPosition, wx.DefaultSize, 0 )
        self.m_staticText5.Wrap( -1 )
        fgs_p.Add( self.m_staticText5, 0, wx.ALL|wx.EXPAND, 5 )
        self.m_textCtrl_pre = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_MULTILINE )
        fgs_p.Add( self.m_textCtrl_pre, 0, wx.ALL|wx.EXPAND, 5 )
        self.m_staticText6 = wx.StaticText( self, wx.ID_ANY, t(u"Postcondición", self.l), wx.DefaultPosition, wx.DefaultSize, 0 )
        self.m_staticText6.Wrap( -1 )
        fgs_p.Add( self.m_staticText6, 0, wx.ALL|wx.EXPAND, 5 )
        self.m_textCtrl_post = wx.TextCtrl( self, wx.ID_ANY, wx.EmptyString, wx.DefaultPosition, wx.DefaultSize, wx.TE_MULTILINE )
        fgs_p.Add( self.m_textCtrl_post, 0, wx.ALL|wx.EXPAND, 5 )
        # FlexGridSizer de cajas de texto.
        fgs = wx.FlexGridSizer(200,3,5,5)
        fgs.AddGrowableCol(0)
        fgs.AddGrowableCol(1)
        fgs.AddGrowableCol(2)
        for i in range(0,199):
            fgs.AddGrowableRow(i)
        fgs.SetFlexibleDirection(wx.BOTH)
        fgs.SetNonFlexibleGrowMode(wx.FLEX_GROWMODE_SPECIFIED)
        # Textos.
        m_texto_p = wx.StaticText( self, wx.ID_ANY, t(u"PARÁMETROS", self.l), wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE )
        m_texto_p.Wrap(-1)
        # Añadimos texto al flexgridsizer.
        fgs.Add(m_texto_p, 0, wx.ALL|wx.EXPAND, 5 )
        # Textos.
        m_texto_v = wx.StaticText( self, wx.ID_ANY, t(u"VALOR POR DEFECTO", self.l), wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE )
        m_texto_v.Wrap(-1)
        # Añadimos texto al flexgridsizer.
        fgs.Add(m_texto_v, 0, wx.ALL|wx.EXPAND, 5 )
        # Textos.
        m_texto_i = wx.StaticText( self, wx.ID_ANY, t(u"VALIDAR TIPO DE DATO COMO", self.l), wx.DefaultPosition, wx.DefaultSize, wx.ALIGN_CENTRE )
        m_texto_i.Wrap(-1)
        # Añadimos texto al flexgridsizer.
        fgs.Add(m_texto_i, 0, wx.ALL|wx.EXPAND, 5 )
        for i in range(0,nfilas):
            # Nombre del parámetro.
            p = wx.TextCtrl(self, -1)
            # Valor por defecto.
            v = wx.TextCtrl(self, -1)
            # Tipo de valor (assert).
            m_choice_tipo_datoChoices = proy_por_defecto['tipo_dato_python']
            ii = wx.Choice( self, wx.ID_ANY, wx.DefaultPosition, wx.DefaultSize, m_choice_tipo_datoChoices, 0 )
            ii.SetSelection( 0 )
            # Incluimos los controles en lista.
            self.control_texto.append((p,v,ii))
            # Añadimos controles al flexgridsizer.
            fgs.Add(p, 0, wx.ALL|wx.EXPAND, 5 )
            fgs.Add(v, 0, wx.ALL|wx.EXPAND, 5 )
            fgs.Add(ii, 0, wx.ALL|wx.EXPAND, 5 )
        # [19-05-2011] Añadimos el flexgridsizer de comentarios 
        # al sizer principal.
        sizer_p.Add(fgs_p, 0, wx.ALL|wx.EXPAND, 5)
        # Añadimos el flexgridsizer al sizer principal.
        sizer_p.Add(fgs, 0, wx.ALL|wx.EXPAND, 5)
        self.SetSizer(sizer_p)
        self.Layout()
        # Binding.
        self.panel_principal.m_choice_funciones.Bind(wx.EVT_UPDATE_UI, self.OnUpdateFunciones)
        # Y el Setup Scrolling del ScrolledPanel!!!
        self.SetupScrolling()

    def OnUpdateFunciones(self, event):
        '''Activar/desactivar áreas del panel de funciones'''
        funcion = self.panel_principal.m_choice_funciones.GetStringSelection()
        if len(funcion.strip()) != 0:
            self.Enable(True)
            self.panel_principal.panel_editor_func.Enable(True)
            return
        if self.IsEnabled() and self.panel_principal.panel_editor_func.IsEnabled():
            # Desactivamos paneles.
            self.Enable(False)
            self.panel_principal.panel_editor_func.Enable(False)
            # Limpiamos.
            self.limpiar_widget()
        
    def __pedir_datos(self, opcion = 0):
        '''Pedir datos mediante un wx.TextEntryDialog.'''
        if opcion == 0: cadena = t(u'Nombre de la nueva función: ', self.l)
        if opcion == 1: cadena = t(u'Renombrar nombre de función: ', self.l)
        dialogo = wx.TextEntryDialog(self, cadena, t(u'Función', self.l))
        dialogo.Show()
        if dialogo.ShowModal() == wx.ID_OK:
            ret = dialogo.GetValue()
        else:
            # Botón cancelar. Nada que devolver.
            ret = None
        # Limpiamos memoria y nos vamos.
        dialogo.Destroy()
        return ret
    
    def OnCrearFuncion( self, event ):
        '''Manejador de eventos para creación de función'''
        # Nombre de la función.
        i = self.__pedir_datos()
        if i is None: return
        nom_func = unicode(i).strip() 
        if len(nom_func) == 0: return
        # ¿Función ya existe?
        for funcion in self.modelo:
            if funcion[0] == nom_func:
                wx.MessageBox(t(u"¡La función ya existe!", self.l), t(u"Atención", self.l), wx.OK)
                return
        # Creamos función en modelo.
        self.modelo.append([nom_func,'','','','',list()])
        # Inluimos función en el choice.
        self.panel_principal.m_choice_funciones.Append(nom_func)

    def OnRenombrarFuncion(self, event):
        '''Manejador de eventos para cambiar el nombre de la función'''
        nom_func_original = self.panel_principal.m_choice_funciones.GetStringSelection()
        if len(nom_func_original) == 0:
            wx.MessageBox(t(u"Se debe de elegir una función a renombrar", self.l), t(u"Atención", self.l), wx.OK)
            return
        # Pedimos nombre de función.
        i = self.__pedir_datos(1)
        if i is None: return
        nom_func = unicode(i).strip()
        if len(nom_func) == 0: return
        # ¿Función ya existe?
        for funcion in self.modelo:
            if funcion[0] == nom_func:
                wx.MessageBox(t(u"¡La función ya existe!", self.l), t(u"Atención", self.l), wx.OK)
                return
        # Cambiamos el nombre en el modelo.
        indice = -1
        for funcion in self.modelo:
            indice += 1
            if funcion[0] == nom_func_original:
                self.modelo[indice][0] = nom_func
                break
        # Cambiamos el nombre en el choice.
        j = self.panel_principal.m_choice_funciones.GetCurrentSelection() 
        self.panel_principal.m_choice_funciones.SetString(j, nom_func)  
        self.panel_principal.m_choice_funciones.SetSelection(j)

    def OnEliminarFuncion(self, event):
        '''Manejador de eventos para eliminar una función'''
        nom_func_original = self.panel_principal.m_choice_funciones.GetStringSelection()
        if len(nom_func_original) == 0:
            wx.MessageBox(t(u"Se debe de elegir una función a eliminar", self.l), t(u"Atención", self.l), wx.OK)
            return
        # Aviso.
        i = wx.MessageBox(t(u"¿Eliminar función?", self.l), t(u"Atención", self.l), wx.YES_NO)
        if i != wx.YES: return
        # Eliminamos nombre en el modelo.
        for funcion in self.modelo:
            if funcion[0] == nom_func_original:
                self.modelo.remove(funcion)
                break
        # Limpiamos...
        self.limpiar_widget()
        # Eliminamos el nombre en el choice.
        j = self.panel_principal.m_choice_funciones.GetCurrentSelection() 
        self.panel_principal.m_choice_funciones.Delete(j) 
        wx.MessageBox(t(u"Se eliminó la función", self.l),t(u"Atención", self.l),wx.OK)

    def OnSetFocusChoiceFuncion(self, event):
        '''Se guardan datos de widgets en modelo de función'''
        self.salvar_funcion()
        event.Skip()

    def OnElegirFuncion(self, event):
        '''Se cargan datos del modelo de función en widgets'''
        # Nombre de la función seleccionada.
        nom_func = self.panel_principal.m_choice_funciones.GetStringSelection()
        # Limpiamos widgets.
        self.limpiar_widget()
        # Cargamos datos en los widgets.
        for funcion in self.modelo:
            if funcion[0] == nom_func:
                self.m_textCtrl_coment.SetValue(funcion[1])
                self.m_textCtrl_post.SetValue(funcion[3])
                self.m_textCtrl_pre.SetValue(funcion[2])
                self.panel_principal.panel_editor_func.panel.editor.SetText(funcion[4])
                indice = -1
                for parametros in funcion[5]:
                    indice += 1
                    self.control_texto[indice][0].SetValue(parametros[0])
                    self.control_texto[indice][1].SetValue(parametros[1])
                    self.control_texto[indice][2].SetStringSelection(parametros[2])
                break
        event.Skip()
        
    def salvar_funcion(self):
        '''Salvamos datos de la función en modelo'''
        nom_func = self.panel_principal.m_choice_funciones.GetStringSelection()
        if len(nom_func.strip()) == 0:
            return
        # Recorremos estructura, recuperando información para guardarla en el 
        # modelo.
        coment = self.m_textCtrl_coment.GetValue()
        post = self.m_textCtrl_post.GetValue()
        pre = self.m_textCtrl_pre.GetValue()
        codigo = self.panel_principal.panel_editor_func.panel.editor.GetText()
        parametros = list()
        for parametro in self.control_texto:
            if len(parametro[0].GetValue().strip()) == 0: continue
            parametros.append((parametro[0].GetValue().strip(),
                               parametro[1].GetValue().strip(),
                               parametro[2].GetStringSelection().strip()))
        # Modificamos el modelo.
        indice = -1
        for funcion in self.modelo:
            indice += 1
            if funcion[0] == nom_func:
                self.modelo[indice][1] = coment
                self.modelo[indice][2] = pre
                self.modelo[indice][3] = post
                self.modelo[indice][4] = codigo
                self.modelo[indice][5] = parametros
                # Salimos.
                break

    def limpiar_widget(self):
        '''Método para limpiar widgets del panel de funciones'''
        # Limpiamos parámetros y comentarios de función
        self.m_textCtrl_coment.SetValue('')
        self.m_textCtrl_post.SetValue('')
        self.m_textCtrl_pre.SetValue('')
        for parametro in self.control_texto:
            parametro[0].SetValue('')
            parametro[1].SetValue('')
            parametro[2].SetSelection(0)
        # Limpiamos el editor de funciones.
        self.panel_principal.panel_editor_func.panel.editor.SetText('')

    def salvar_datos(self):
        '''Método que obtiene el modelo de funciones y lo devuelve en un diccionario'''
        funciones = {'funciones' : self.modelo}
        # Devolvemos diccionario.
        return funciones
        
    def cargar_datos(self, dict_funciones):
        '''Cargamos funciones en modelo y widget choice'''
        if len(dict_funciones) != 0:
            funciones = dict_funciones['funciones']
            # Asignamos el modelo de funciones.
            self.modelo = funciones
            # Rellenamos el choice de funciones con nombres.
            lista_nombres_funciones = list()
            for funcion in self.modelo:
                lista_nombres_funciones.append(funcion[0])
                self.panel_principal.m_choice_funciones.SetItems(lista_nombres_funciones)
        else:
            self.modelo = list()
            self.panel_principal.m_choice_funciones.SetItems(list())
    